﻿using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System;
using LightPersist.Exceptions;

namespace LightPersist
{
	/// <summary>
	/// This interface is published to model
	/// </summary>
	/// <typeparam name="T"></typeparam>
	public interface ILightListProxy<T> : IList<T>, INotifyCollectionChanged, ILightListProxy
	{
		T AddNew();
	}

	public interface ILightListProxy
	{
		ModelBase Owner { get; }
	}

	/// <summary>
	/// This is for internal implementation accessing without generic parameter
	/// </summary>
	interface ILightListProxyInternal
	{
		void OnCollectionChanged(NotifyCollectionChangedEventArgs args);
	}

	sealed class LightListProxy<T> : ILightListProxy<T>, ILightListProxyInternal
	{
		readonly LightListCore _core;

		public T AddNew()
		{
			T item;
			if (typeof(ModelBase).IsAssignableFrom(typeof(T)))
			{
				item = (T)Core.Context.CreateInternal(typeof(T));
				Add(item);
			}
			else
			{
				if (typeof(T).IsValueType)
				{
					item = default(T);
				}
				else
				{
					item = Activator.CreateInstance<T>();
				}
			}
			return item;
		}

		LightListProxy(LightListCore core)
		{
			_core = core;
		}

		LightListCore Core
		{
			get { return _core; }
		}

		#region ILightListProxy<T> Members

		public int IndexOf(T item)
		{
			var mb = item as ModelBase;
			if (mb != null)
			{
				return Core.IndexOf(mb);
			}
			else
			{
				for (int i = 0, c = Core.Count; i < c; i++)
				{
					var element = Core[i] as ValueModelWrapper;
					if (element != null)
					{
						if (Equals(element.Value, item))
						{
							return i;
						}
					}
				}
				return -1;
			}
		}

		public void Insert(int index, T item)
		{
			var mb = item as ModelBase;
			if (mb != null)
			{
				Core.Insert(index, mb);
			}
			else
			{
				Core.Insert(index, Core.Context.CreateValueWrapper(item));
			}
		}

		public void RemoveAt(int index)
		{
			Core.RemoveAt(index);
		}

		public T this[int index]
		{
			get
			{
				var item = Core[index];
				var valueWrapper = item as ValueModelWrapper;
				if (valueWrapper != null)
				{
					return (T)valueWrapper.Value;
				}
				else
				{
					return (T)(object)item;
				}
			}
			set
			{
				var mb = value as ModelBase;
				if (mb != null)
				{
					Core[index] = mb;
				}
				else
				{
					Core[index] = Core.Context.CreateValueWrapper(value);
				}
			}
		}

		public void Add(T item)
		{
			var mb = item as ModelBase;
			if (mb != null)
			{
				Core.Add(mb);
			}
			else
			{
				Core.Add(Core.Context.CreateValueWrapper(item));
			}
		}

		public void Clear()
		{
			Core.Clear();
		}

		public bool Contains(T item)
		{
			return Core.FirstOrDefault(x => UniversalCompare(item, x)) != null;
		}

		public void CopyTo(T[] array, int arrayIndex)
		{
			for (int i = 0, c = Core.Count; i < c; i++)
			{
				array[i + arrayIndex] = this[i];
			}
		}

		public int Count
		{
			get { return Core.Count; }
		}

		public bool IsReadOnly
		{
			get { return Core.IsReadOnly; }
		}

		public bool Remove(T item)
		{
			var mb = item as ModelBase;
			if (mb != null)
			{
				return Core.Remove(mb);
			}
			else
			{
				var i = IndexOf(item);
				if (i == -1)
				{
					return false;
				}
				else
				{
					Core.RemoveAt(i);
					return true;
				}
			}
		}

		public IEnumerator<T> GetEnumerator()
		{
			return Core.Cast<T>().GetEnumerator();
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return Core.GetEnumerator();
		}

		public event NotifyCollectionChangedEventHandler CollectionChanged;

		public ModelBase Owner
		{
			get { return Core.Owner; }
		}

		#endregion

		#region ILightListProxyInternal Members

		public void OnCollectionChanged(NotifyCollectionChangedEventArgs args)
		{
			if (CollectionChanged != null)
			{
				CollectionChanged(this, args);
			}
		}

		#endregion

		static bool UniversalCompare(object item, ModelBase element)
		{
			var w = element as ValueModelWrapper;
			if (w != null)
			{
				return Equals(w.Value, item);
			}

			var m = item as ModelBase;
			if (m != null)
			{
				return Equals(element, m);
			}

			throw new CriticalInternalLightPersistException();
		}

		internal static ILightListProxy<T> Factory(LightListCore core)
		{
			LightListProxy<T> result;
			if (core.LightListProxyInternal == null)
			{
				core.LightListProxyInternal = result = new LightListProxy<T>(core);
			}
			else
			{
				result = (LightListProxy<T>)core.LightListProxyInternal;
			}
			return result;
		}

		public override int GetHashCode()
		{
			return Core.GetHashCode();
		}

		public override bool Equals(object obj)
		{
			return Core.Equals(obj);
		}
	}
}